/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.generator.repository.adaptor.method.createpropertydefmethod;

//import Inspur.Gsp.Caf.Generator.Utils.*;
//import Inspur.Gsp.Cef.Repository.*;
//import Inspur.Gsp.Cef.Repository.ReaderWriter.*;
//import Microsoft.CodeAnalysis.*;
//import Microsoft.CodeAnalysis.CSharp.*;
//import Microsoft.CodeAnalysis.CSharp.Syntax.*;

import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.caf.generator.baseInfo.ParameterInfo;
import com.inspur.edp.caf.generator.baseInfo.TypeInfo;
import com.inspur.edp.caf.generator.method.ClassMethodGenerator;
import com.inspur.edp.cef.api.attr.PropertyGetAtrribute;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspAssociationKey;
import com.inspur.edp.cef.gencommon.ReflectionUtils;
import com.inspur.edp.cef.generator.coreGenerator.base.CefCoreGeneratorContext;
import com.inspur.edp.cef.generator.overall.CefGeneratorContext;
import com.inspur.edp.cef.generator.overall.GeneratorUtil;
import com.inspur.edp.cef.generator.repository.RepositoryGenUtil;
import com.inspur.edp.cef.repository.adaptor.EntityRelationalAdaptor;
import com.inspur.edp.cef.repository.assembler.AssociationInfo;
import com.inspur.edp.cef.repository.readerwriter.CefMappingReader;
import com.inspur.edp.cef.repository.repo.BaseRootRepository;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.dbInfo.ColumnMapType;
import org.eclipse.jdt.core.dom.*;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//      private HashMap<String, String> getF1AssocDBMapping() {
//////		HashMap<String, String> mapping = new HashMap<String, String>();
//////		mapping.put("F2", "F1_F2");
//////		HashMap<String, String> cloneMap = (HashMap<String, String>) mapping.clone();
//////		EntityRelationalAdaptor adapter = ((BaseRootRepository) getAssociation("F1").getRefRepository()).getEntityDac("DemoSalesOrder2BE").getEntityAdaptor();
//////		HashMap<String, String> f2Mapping = adapter.getAssocPropDBMapping("F2");
//////
//////		for (Map.Entry<String, String> map : f2Mapping.entrySet()) {
//////		if (!mapping.containsKey(map.getValue())) {
//////		mapping.put(map.getValue(), "F1_" + map.getValue());
//////		}
//////		}
//////		return mapping;
//////		}

public class EntityGetAssocationDBMappingMethodGenerator extends ClassMethodGenerator {
	private GspAssociation associationInfo;
	private IGspCommonDataType gspCommonDataType;
	private CefGeneratorContext cefGeneratorContext;
	private Class propertyType;
	private String propertyName;
	private static final String varName = "assValue";
	public static final String varMapping = "mapping";
	private static final String varAdapter = "adapter";

	public EntityGetAssocationDBMappingMethodGenerator(CefGeneratorContext cefGeneratorContext, IGspCommonDataType gspCommonDataType, GspAssociation associationInfo, String propertyName) {
		this.cefGeneratorContext = cefGeneratorContext;
		this.gspCommonDataType = gspCommonDataType;
		this.associationInfo = associationInfo;
		this.propertyName = propertyName;
	}

	@Override
	protected String getMethodName() {
		return getAssPropertyValueMethodName(propertyName);
	}

	@Override
	protected TypeInfo getReturnType() {
		TypeInfo typeInfo = new TypeInfo(HashMap.class);
		typeInfo.setIsParameterizedType(true);
		List<TypeInfo> listType = new ArrayList<TypeInfo>();
		listType.add(new TypeInfo(String.class));
		listType.add(new TypeInfo(String.class));
		typeInfo.setArguments(listType);
		return typeInfo;
	}

	@Override
	protected ArrayList<Modifier.ModifierKeyword> getAccessModifiers() {
		ArrayList<Modifier.ModifierKeyword> list =new ArrayList<>();
		list.add(Modifier.ModifierKeyword.PRIVATE_KEYWORD);
		return list;
	}

	@Override
	protected ArrayList<ParameterInfo> getParameterCollection() {
		return null;
	}

	@Override
	protected Block buildMethodBody() {
		ArrayList<Statement> statements = new ArrayList<>();
		addImport(CefMappingReader.class.getTypeName());
		addImport(BaseRootRepository.class.getTypeName());
		addImport(Map.class.getTypeName());
		addInitMappingStatement(ast,cefGeneratorContext, gspCommonDataType, statements,  associationInfo, varMapping);
//		addNewMappingReaderStatement(ast, statements, getMappingReader(propertyName), varMapping);
		addNewAdapterStatement(ast, statements, propertyName, varAdapter, this.associationInfo.getRefObjectCode());
		addGetDBMappingStatement(ast, statements, associationInfo, cefGeneratorContext, gspCommonDataType);
		addReturnInfoStatement(statements);
		Block block = ast.newBlock();
		block.statements().addAll(statements);
		return block;
	}

	private void addReturnInfoStatement(ArrayList<Statement> statements) {
		ReturnStatement returnStatement = ast.newReturnStatement();

		returnStatement.setExpression(ast.newSimpleName("mapping"));
		statements.add(returnStatement);
	}

	public static void addInitMappingStatement(AST ast, CefGeneratorContext cefGeneratorContext, IGspCommonDataType gspCommonDataType, ArrayList<Statement> statements, GspAssociation associationInfo, String varMappingName) {
		addNewMappingStatement(ast, statements, varMappingName);
		addSetMappingItemsStatements(ast, cefGeneratorContext, gspCommonDataType, statements, associationInfo, varMappingName);
	}

	private static void addNewMappingStatement(AST ast, ArrayList<Statement> statements, String varMapping) {
		VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName(varMapping));

		ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
		ParameterizedType parameterizedType1 = ast.newParameterizedType(new TypeInfo(HashMap.class).getType(ast));
		parameterizedType1.typeArguments().add(new TypeInfo(String.class).getType(ast));
		parameterizedType1.typeArguments().add(new TypeInfo(String.class).getType(ast));
		classInstanceCreation.setType(parameterizedType1);

		variableDeclarationFragment.setInitializer(classInstanceCreation);

		ParameterizedType parameterizedType = ast.newParameterizedType(new TypeInfo(HashMap.class).getType(ast));
		parameterizedType.typeArguments().add(new TypeInfo(String.class).getType(ast));
		parameterizedType.typeArguments().add(new TypeInfo(String.class).getType(ast));
		VariableDeclarationStatement variableDeclarationStatement = ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(parameterizedType);
		statements.add(variableDeclarationStatement);
	}

	private static IGspCommonField getRefIGspCommonField(String elemnetId, IGspCommonObject gspCommonObject){
		IGspCommonField gspCommonField = null;
		for(IGspCommonField field:gspCommonObject.getContainElements()){
			if(field.getID().equals(elemnetId)){
				gspCommonField = field;
				return gspCommonField;
			}
		}
		for(IGspCommonField field:gspCommonObject.getContainElements()){
			if(field.getChildAssociations() !=null && field.getChildAssociations().size() > 0){
				for (IGspCommonField commonField: field.getChildAssociations().get(0).getRefElementCollection()){
					if(commonField.getID().equals(elemnetId))
						return commonField;
				}
			}
		}
		return gspCommonField;
	}

	private static void addSetMappingItemsStatements(AST ast, CefGeneratorContext cefGeneratorContext, IGspCommonDataType gspCommonDataType, ArrayList<Statement> statements, GspAssociation gspAssociation, String varMapping) {
		AssociationInfo associationInfo =  getAssociationInfo(cefGeneratorContext, gspAssociation, gspCommonDataType);
		GspMetadata metadata = cefGeneratorContext.getMetadataJitContext().
				getMetadata(gspAssociation.getRefModelID());
		GspBusinessEntity gspBusinessEntity = (GspBusinessEntity)metadata.getContent();
		IGspCommonObject refObject = gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID());
		for(IGspCommonField commonField:gspAssociation.getRefElementCollection()){
			IGspCommonField refField = getRefIGspCommonField(commonField.getRefElementId(), refObject);
//			if (refField == null || associationInfo.getTargetColumn().equals(refField.getLabelID())) {
			if (refField == null) {
				continue;
			}


			MethodInvocation methodInvocation = ast.newMethodInvocation();
			methodInvocation.setExpression(ast.newSimpleName(varMapping));
			methodInvocation.setName(ast.newSimpleName("put"));
			methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, refField.getLabelID()));
			String preFiex = "";
			if(gspAssociation.getBelongElement().getIsUdt() && commonField.getIsFromAssoUdt()){//关联udt
				if(gspAssociation.getBelongElement().getChildElements() != null && gspAssociation.getBelongElement().getChildElements().size() >0){
					preFiex = gspAssociation.getBelongElement().getChildElements().get(0).getLabelID();
					methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, preFiex + "_" + refField.getLabelID()));
				}
				else {//兼容处理childElements为空的情况
					methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, commonField.getLabelID()));
				}
			}
			else {
				if(commonField.getIsUdt()){
					UnifiedDataTypeDef udtDef=(UnifiedDataTypeDef)cefGeneratorContext.getMetadataJitContext().getMetadata(commonField.getUdtID()).getContent();
					if(udtDef instanceof ComplexDataTypeDef && ((ComplexDataTypeDef)udtDef).getDbInfo().getMappingType() == ColumnMapType.MultiColumns){
						if(commonField.getChildElements() != null && commonField.getChildElements().size()>0){
							for(IGspCommonField childField: commonField.getChildElements()){
								MethodInvocation childmethodInvocation = ast.newMethodInvocation();
								childmethodInvocation.setExpression(ast.newSimpleName(varMapping));
								childmethodInvocation.setName(ast.newSimpleName("put"));
								//todo 用getCode合适吗
								childmethodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, childField.getCode()));
								childmethodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, associationInfo.getTargetColumn() + "_" + childField.getCode()));
								statements.add(ast.newExpressionStatement(childmethodInvocation));
							}
						}
						continue;
					}
				}
				methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, commonField.getLabelID()));
			}

			statements.add(ast.newExpressionStatement(methodInvocation));
		}

		{
			MethodInvocation methodInvocation = ast.newMethodInvocation();
			methodInvocation.setExpression(ast.newSimpleName(varMapping));
			methodInvocation.setName(ast.newSimpleName("put"));

			methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, associationInfo.getSourceColumn()));
			methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, associationInfo.getTargetColumn()));
			statements.add(ast.newExpressionStatement(methodInvocation));

//			((DataTypeAdapter)associationInfo.getRefRepository().getMainDac().getAdaptor()).
		}
	}

	//	CefMappingReader readerName = new CefMappingReader(mapping, reader);
	public static void addNewMappingReaderStatement(AST ast, ArrayList<Statement> statements, String readerName, String mappingName) {
		VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName(readerName));

		ClassInstanceCreation classInstanceCreation = ast.newClassInstanceCreation();
		classInstanceCreation.setType(new TypeInfo(CefMappingReader.class).getType(ast));
		ArrayList arguments = new ArrayList();
		arguments.add(RepositoryGenUtil.createVariableLiteral(ast, mappingName));
//		arguments.add(RepositoryGenUtil.createVariableLiteral(ast, ParamReader));
		classInstanceCreation.arguments().addAll(arguments);
		variableDeclarationFragment.setInitializer(classInstanceCreation);

		VariableDeclarationStatement variableDeclarationStatement = ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(new TypeInfo(CefMappingReader.class).getType(ast));
		statements.add(variableDeclarationStatement);
	}

	//	EntityRelationalAdaptor adapter=( (BaseRootRepository)( getAssociation("DeptID").RefRepository )).getEntityDac("").getEntityAdaptor();
	public static void addNewAdapterStatement(
			AST ast,
			ArrayList<Statement> statements,
			String propertyName,
			String adaperName,
			String nodeCode) {
		VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName(adaperName));
		addNewAdapterAdapterVarInitializer(ast, variableDeclarationFragment, propertyName, nodeCode);

		VariableDeclarationStatement variableDeclarationStatement = ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(new TypeInfo(EntityRelationalAdaptor.class).getType(ast));

		statements.add(variableDeclarationStatement);

	}

	private static void addNewAdapterAdapterVarInitializer(
			AST ast,
			VariableDeclarationFragment variableDeclarationFragment,
			String propertyName,
			String nodeCode) {
		MethodInvocation methodInvocation = ast.newMethodInvocation();
		methodInvocation.setName(ast.newSimpleName("getEntityAdaptor"));
		addgetEntityAdapterExpression(ast, methodInvocation, propertyName, nodeCode);
		variableDeclarationFragment.setInitializer(methodInvocation);
	}

	private static void addgetEntityAdapterExpression(AST ast, MethodInvocation methodInvocation, String propertyName, String nodeCode) {
		MethodInvocation methodInvocationGetDac = ast.newMethodInvocation();
		methodInvocationGetDac.setName(ast.newSimpleName("getEntityDac"));
		methodInvocationGetDac.arguments().add(GeneratorUtil.getStringLiteral(ast, nodeCode));
		addgetEntityDacExpression(ast, methodInvocationGetDac, propertyName);

		methodInvocation.setExpression(methodInvocationGetDac);

	}

	private static void addgetEntityDacExpression(AST ast, MethodInvocation methodInvocationGetDac, String propertyName) {
		CastExpression castExpression = ast.newCastExpression();
		castExpression.setType(new TypeInfo(BaseRootRepository.class).getType(ast));
		addgetRefRepositoryMethod(ast, castExpression, propertyName);

		ParenthesizedExpression parenthesizedExpression = ast.newParenthesizedExpression();
		parenthesizedExpression.setExpression(castExpression);
		methodInvocationGetDac.setExpression(parenthesizedExpression);
	}

	private static void addgetRefRepositoryMethod(AST ast, CastExpression castExpression, String propertyName) {
		MethodInvocation methodInvocation = ast.newMethodInvocation();
		methodInvocation.setName(ast.newSimpleName("getRefRepository"));

		MethodInvocation methodInvocationGetAssociation = ast.newMethodInvocation();
		methodInvocationGetAssociation.setName(ast.newSimpleName("getAssociation"));
		methodInvocationGetAssociation.arguments().add(GeneratorUtil.getStringLiteral(ast, propertyName));

		methodInvocation.setExpression(methodInvocationGetAssociation);
		castExpression.setExpression(methodInvocation);
	}

	public static AssociationInfo getAssociationInfo(CefGeneratorContext cefGeneratorContext, GspAssociation gspAssociation, IGspCommonDataType gspCommonDataType){
		AssociationInfo associationInfo = new AssociationInfo();
		String sourceElementId = "";
		String targetElementId = "";
		for(GspAssociationKey key: gspAssociation.getKeyCollection()){
			sourceElementId = key.getSourceElement();
			targetElementId = key.getTargetElement();
		}
		for(IGspCommonField gspCommonField: gspCommonDataType.getContainElements()){
			if(gspCommonField.getID().equals(targetElementId)){
				associationInfo.setTargetColumn(gspCommonField.getLabelID());
				break;
			}
		}

		//获取target
		GspMetadata metadata = cefGeneratorContext.getMetadataJitContext().
				getMetadata(gspAssociation.getRefModelID());
		GspBusinessEntity gspBusinessEntity = (GspBusinessEntity)metadata.getContent();
		IGspCommonObject refObject = gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID());
		for(IGspCommonField refField:refObject.getContainElements()){
			if(refField.getID().equals(sourceElementId)){
				associationInfo.setSourceColumn(refField.getLabelID());
				break;
			}
		}
		return associationInfo;
	}

	private static void addGetDBMappingStatement(AST ast, ArrayList<Statement> statements, GspAssociation gspAssociation, CefGeneratorContext cefGeneratorContext, IGspCommonDataType gspCommonDataType) {
		AssociationInfo associationInfo = getAssociationInfo(cefGeneratorContext, gspAssociation, gspCommonDataType);
		GspMetadata metadata = cefGeneratorContext.getMetadataJitContext().
				getMetadata(gspAssociation.getRefModelID());
		GspBusinessEntity gspBusinessEntity = (GspBusinessEntity)metadata.getContent();
		IGspCommonObject refObject = gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID());
		for(IGspCommonField commonField:gspAssociation.getRefElementCollection()){
			RefAssociationInfo refAssociationInfo = getRefAssociation(cefGeneratorContext, commonField, gspAssociation);
			if(commonField.getLabelID().equals(associationInfo.getTargetColumn()))
				continue;
			commonField.getRefElementId();

			String propName = "";
			for(IGspCommonField refField:refObject.getContainElements()){
				if(refField.getID().equals(commonField.getRefElementId())){
					propName = refField.getLabelID();
					break;
				}
			}
			if(propName.isEmpty()){
				propName = refAssociationInfo.getField().getLabelID();
			}
			if(propName==null||propName.isEmpty())
				continue;
			addGetAssoPropDBMappingStatement(ast, statements, propName);
			addPutEnhancedForStatement(ast, statements, propName, associationInfo.getTargetColumn());
		}


//		for (Map.Entry<String, String> item : associationInfo.getRefColumns().entrySet()) {
//			if (associationInfo.getTargetColumn().equals(item.getValue())) {
//				continue;
//			}
//			addGetAssoPropDBMappingStatement(ast, statements, item.getValue());
//			addPutEnhancedForStatement(ast, statements, item.getValue(), associationInfo.getSourceColumn());
//		}
	}

	private static RefAssociationInfo getRefAssociation(CefGeneratorContext cefGeneratorContext, IGspCommonField field, GspAssociation gspAssociation){
		RefAssociationInfo refAssociationInfo = new RefAssociationInfo();
		GspAssociation refAssociation = null;
		GspMetadata metadata = cefGeneratorContext.getMetadataJitContext().
				getMetadata(gspAssociation.getRefModelID());
		GspBusinessEntity gspBusinessEntity = (GspBusinessEntity)metadata.getContent();
		IGspCommonObject refObject = gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID());
		for(IGspCommonField commonField: refObject.getContainElements()){
			if(commonField.getID().equals(field.getRefElementId())){
				refAssociationInfo.setRefField(commonField);
				refAssociationInfo.setField(field);
				refAssociationInfo.setGspAssociation(gspAssociation);
				return refAssociationInfo;
			}
		}
		//说明是关联带出关联字段
		IGspCommonField tempField = refObject.findElement(field.getRefElementId());
		if(tempField != null){
			refAssociationInfo = getRefAssociation(cefGeneratorContext, tempField);
			if(refAssociationInfo.getGspAssociation() != null){
				return refAssociationInfo;
			}
		}
		return refAssociationInfo;
	}

	private static RefAssociationInfo getRefAssociation(CefGeneratorContext cefGeneratorContext, IGspCommonField refElementField){
		RefAssociationInfo refAssociationInfo = new RefAssociationInfo();
		GspAssociation gspAssociation = refElementField.getParentAssociation();
		if(gspAssociation != null){
			GspMetadata metadata = cefGeneratorContext.getMetadataJitContext().
					getMetadata(gspAssociation.getRefModelID());
			GspBusinessEntity gspBusinessEntity = (GspBusinessEntity)metadata.getContent();
			IGspCommonObject refObject = gspBusinessEntity.findObjectById(gspAssociation.getRefObjectID());
			for(IGspCommonField field: refObject.getContainElements()){
				if(field.getID().equals(refElementField.getRefElementId())){
					refAssociationInfo.setRefField(field);
					refAssociationInfo.setGspAssociation(gspAssociation);
					refAssociationInfo.setField(refElementField);
					return refAssociationInfo;
				}
			}
			IGspCommonField tempField = refObject.findElement(refElementField.getRefElementId());
			if(tempField != null){
				refAssociationInfo = getRefAssociation(cefGeneratorContext, tempField);
				if(refAssociationInfo.getGspAssociation() != null)
					return refAssociationInfo;
			}
		}
		return refAssociationInfo;
	}

	private  static void addGetAssoPropDBMappingStatement(AST ast, ArrayList<Statement> statements, String propertyName){
		VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
		variableDeclarationFragment.setName(ast.newSimpleName(propertyName + "DBMappingMap"));

		MethodInvocation methodInvocation = ast.newMethodInvocation();
		methodInvocation.setName(ast.newSimpleName("getAssosPropDBMapping"));
		methodInvocation.setExpression(ast.newSimpleName("adapter"));
		methodInvocation.arguments().add(GeneratorUtil.getStringLiteral(ast, propertyName));
		variableDeclarationFragment.setInitializer(methodInvocation);

		ParameterizedType parameterizedType = ast.newParameterizedType(new TypeInfo(HashMap.class).getType(ast));
		parameterizedType.typeArguments().add(new TypeInfo(String.class).getType(ast));
		parameterizedType.typeArguments().add(new TypeInfo(String.class).getType(ast));
		VariableDeclarationStatement variableDeclarationStatement = ast.newVariableDeclarationStatement(variableDeclarationFragment);
		variableDeclarationStatement.setType(parameterizedType);
		statements.add(variableDeclarationStatement);
	}

	private static void addPutEnhancedForStatement(AST ast, ArrayList<Statement> statements, String propertyName, String sourceField){
		EnhancedForStatement enhancedForStatement = ast.newEnhancedForStatement();

		SingleVariableDeclaration singleVariableDeclaration = ast.newSingleVariableDeclaration();
		addEnhancedForParameterStatement(ast, singleVariableDeclaration);
		enhancedForStatement.setParameter(singleVariableDeclaration);

		MethodInvocation methodInvocation = ast.newMethodInvocation();
		addEnhancedExpressionStatement(ast, methodInvocation, propertyName);
		enhancedForStatement.setExpression(methodInvocation);

		Block block = ast.newBlock();
		addEnhancedBlockStatement(ast, block, sourceField);
		enhancedForStatement.setBody(block);
		statements.add(enhancedForStatement);
	}

	private static void addEnhancedForParameterStatement(AST ast, SingleVariableDeclaration singleVariableDeclaration)
	{
		QualifiedName qualifiedName = ast.newQualifiedName(ast.newSimpleName("Map"), ast.newSimpleName("Entry"));
		SimpleType simpleType = ast.newSimpleType(qualifiedName);
		ParameterizedType parameterizedType = ast.newParameterizedType(simpleType);
		parameterizedType.typeArguments().add(new TypeInfo(String.class).getType(ast));
		parameterizedType.typeArguments().add(new TypeInfo(String.class).getType(ast));
		singleVariableDeclaration.setType(parameterizedType);
		singleVariableDeclaration.setName(ast.newSimpleName("item"));
	}

	private static void addEnhancedExpressionStatement(AST ast, MethodInvocation methodInvocation, String propertyName){
		methodInvocation.setExpression(ast.newSimpleName(propertyName + "DBMappingMap"));
		methodInvocation.setName(ast.newSimpleName("entrySet"));
	}

	private static void addEnhancedBlockStatement(AST ast, Block block, String sourceField){
		IfStatement ifStatement = ast.newIfStatement();
		addPrefixExpressionStatement(ast, ifStatement);
		addThenStatement(ast, ifStatement, sourceField);
		block.statements().add(ifStatement);
	}

	private static void addPrefixExpressionStatement(AST ast, IfStatement ifStatement){
		MethodInvocation methodInvocation = ast.newMethodInvocation();
		methodInvocation.setName(ast.newSimpleName("containsKey"));
		methodInvocation.setExpression(ast.newSimpleName("mapping"));
		MethodInvocation innerMethodInvocation = ast.newMethodInvocation();
		innerMethodInvocation.setExpression(ast.newSimpleName("item"));
		innerMethodInvocation.setName(ast.newSimpleName("getValue"));
		methodInvocation.arguments().add(innerMethodInvocation);

		PrefixExpression prefixExpression = ast.newPrefixExpression();
		prefixExpression.setOperator(PrefixExpression.Operator.NOT);
		prefixExpression.setOperand(methodInvocation);
		ifStatement.setExpression(prefixExpression);
	}

	private static void addThenStatement(AST ast, IfStatement ifStatement, String sourceField){
		MethodInvocation innerMethodInvocation = ast.newMethodInvocation();
		innerMethodInvocation.setExpression(ast.newSimpleName("item"));
		innerMethodInvocation.setName(ast.newSimpleName("getValue"));

		InfixExpression infixExpression = ast.newInfixExpression();
		infixExpression.setLeftOperand(GeneratorUtil.getStringLiteral(ast, sourceField + "_"));
		infixExpression.setOperator(InfixExpression.Operator.PLUS);
		MethodInvocation rightMethodInvocation = ast.newMethodInvocation();
		rightMethodInvocation.setExpression(ast.newSimpleName("item"));
		rightMethodInvocation.setName(ast.newSimpleName("getValue"));
		infixExpression.setRightOperand(rightMethodInvocation);

		MethodInvocation methodInvocation = ast.newMethodInvocation();
		methodInvocation.setExpression(ast.newSimpleName("mapping"));
		methodInvocation.setName(ast.newSimpleName("put"));
		methodInvocation.arguments().add(innerMethodInvocation);
		methodInvocation.arguments().add(infixExpression);

		ExpressionStatement expressionStatement = ast.newExpressionStatement(methodInvocation);
		Block block = ast.newBlock();
		block.statements().add(expressionStatement);
		ifStatement.setThenStatement(block);
	}

	public static void addSetInfoPropertyStatemens(AST ast, ArrayList<Statement> statements, AssociationInfo associationInfo, Class propertyType, String propertyName) {
		for (Method method : propertyType.getMethods()) {
			PropertyGetAtrribute propertyGetAtrribute = (PropertyGetAtrribute) ReflectionUtils.getMethodAnnotation(method, PropertyGetAtrribute.class);
			if (propertyGetAtrribute == null)
				continue;
			addSetInfoPropertyStatement(ast, propertyGetAtrribute, statements, propertyName, associationInfo,method);
//			BuildPropAssStatement(statements, associationInfo, refProp, propertyName);
		}
	}

	private static void addSetInfoPropertyStatement(AST ast, PropertyGetAtrribute propertyGetAtrribute, ArrayList<Statement> statements, String propertyName, AssociationInfo associationInfo, Method method) {
		MethodInvocation methodInvocation = ast.newMethodInvocation();
		methodInvocation.setName(ast.newSimpleName("set" + propertyGetAtrribute.propName()));
		methodInvocation.setExpression(ast.newSimpleName(varName));
		addSetInfoPropertySetPropertyArguments(ast, propertyGetAtrribute, methodInvocation, propertyName, associationInfo,method);

		statements.add(ast.newExpressionStatement(methodInvocation));
	}

	private static void addSetInfoPropertySetPropertyArguments(AST ast, PropertyGetAtrribute propertyGetAtrribute, MethodInvocation methodInvocation, String propertyName, AssociationInfo associationInfo, Method method) {
		CastExpression castExpression=ast.newCastExpression();

		MethodInvocation methodInvocation1 = ast.newMethodInvocation();
		methodInvocation1.setName(ast.newSimpleName("readProperty"));
		methodInvocation1.setExpression(ast.newSimpleName("adapter"));
		methodInvocation1.arguments().add(GeneratorUtil.getStringLiteral(ast, getRefColumn(propertyGetAtrribute.propName(), associationInfo)));
		methodInvocation1.arguments().add(ast.newSimpleName(getMappingReader(propertyName)));

		castExpression.setExpression(methodInvocation1);
		castExpression.setType(ast.newSimpleType(ast.newName(method.getReturnType().getTypeName())));
		methodInvocation.arguments().add(castExpression);
	}

	public static String getRefColumn(String souceName, AssociationInfo associationInfo) {
		if (souceName.equals(associationInfo.getSourceColumn())) {
			return associationInfo.getTargetColumn();
		}
		return associationInfo.getRefColumns().get(souceName);
	}

	public static String getMappingReader(String propertyName) {
		return "mappingReader" + propertyName;
	}


	public static String getAssPropertyValueMethodName(String propertyName) {
		return "get" + propertyName + "AssoDBMapping";
	}

}
